#include "DxApp.h"
#include "MainLoop.h"
#include "../Buffer/ZBuffer.h"
#include <stdio.h>
HINSTANCE DxApp::_instance = NULL;
ILoop* s_looper = NULL;

DxApp::DxApp(int width,int height){
	_w = width;
	_h = height;
	_looper = NULL;
	_zBuffer = NULL;
	_cullFront = IPixelDrawer::CullNone;
	memset(&lr, 0, sizeof(lr));
}

DxApp* DxApp::create(HINSTANCE instance,int width,int height){
	_instance = instance;
	DxApp* p = new DxApp(width,height);
	p->initConsole();
	p->initWnd();
	p->initD3D();
	p->initZBuffer();
	return p;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
	PAINTSTRUCT ps;
	HDC hdc;

	switch(msg)
	{
	case WM_CREATE: 
		{
			return 0;
		} break;
	case WM_KEYDOWN:
		{
			if (s_looper)
			{
				s_looper->onKeyDown(wParam);
			}
			if (wParam == VK_ESCAPE)
			{
				PostMessage((HWND)lParam, WM_DESTROY, 0, 0);
			} break;
		}break;
	case WM_MOUSEMOVE:{
			WORD xPos = LOWORD(lParam);  // horizontal position of cursor 
			WORD yPos = HIWORD(lParam);  // vertical position of cursor 
			if (s_looper)
			{
				s_looper->onMouseMove(xPos,yPos);
			}
		}break;
	case WM_LBUTTONDOWN:{
			WORD xPos = LOWORD(lParam);  // horizontal position of cursor 
			WORD yPos = HIWORD(lParam);  // vertical position of cursor 
			if (s_looper)
			{
				s_looper->onMouseDown(xPos,yPos);
			}
		}break;
	case WM_PAINT:
		{
			hdc = BeginPaint(hwnd, &ps);
			EndPaint(hwnd, &ps);
			return 0;
		} break;
	case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		} break;
	default:break;
	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}
void DxApp::initWnd()
{
	WNDCLASS winclass;

	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	winclass.lpfnWndProc = WindowProc;
	winclass.cbClsExtra = 0;
	winclass.cbWndExtra = 0;
	winclass.hInstance = _instance;
	winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winclass.lpszMenuName = NULL;
	winclass.lpszClassName = "YF3D";

	RegisterClass(&winclass);

	int sw = GetSystemMetrics(SM_CXSCREEN);
	int sh = GetSystemMetrics(SM_CYSCREEN);
	_wnd = CreateWindow("YF3D",
		"yafacex's 3d lab",
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		(sw-_w)/2, (sh-_h)/2,
		_w, _h,
		NULL,
		NULL,
		_instance,
		NULL);
}
void DxApp::initD3D(){
	IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

	D3DPRESENT_PARAMETERS d3dpp;
	d3dpp.BackBufferWidth            = _w;
	d3dpp.BackBufferHeight           = _h;
	d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;
	d3dpp.BackBufferCount            = 1;
	d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality         = 0;
	d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow              = _wnd;
	d3dpp.Windowed                   = TRUE;
	d3dpp.EnableAutoDepthStencil     = true;
	d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;
	d3dpp.Flags                      = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;

	HRESULT res = d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, _wnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &_device);
	d3d9->Release();

	_device->CreateOffscreenPlainSurface(_w, _h, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &_surface, 0);
}
void DxApp::dealMsg(){
	MSG msg;
	msg.lParam = (LPARAM)this;
	while(TRUE)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		if (_looper)
		{
			_looper->loop(this);
		}
	}
}

void DxApp::drawPixel(int x,int y,COLOR color){
	if (x < 0 || y < 0 || x >= _w || y >= _h)
	{
		return;
	}

	//color = 0x55ff0000;//ARGB 
	//color = RGBA(0,128,128,0);
	DWORD* pBits = (DWORD*)lr.pBits;
	int i = x + y * (lr.Pitch >> 2);
	pBits[i] = color;

}


void DxApp::setLooper(ILoop* looper){
	_looper = looper;
	s_looper = looper;
}

void DxApp::initConsole()
{
	AllocConsole();
	freopen("CONIN$", "r", stdin);
	freopen("CONOUT$", "w", stdout);
	freopen("CONOUT$", "w", stderr);
}

void DxApp::flushBuffer()
{
	_surface->UnlockRect();	
	IDirect3DSurface9* backBuffer = 0;
	//get buffer
	_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);

	//fill buffer
	_device->StretchRect(_surface, 0, backBuffer, 0, D3DTEXF_NONE);
	backBuffer->Release();
	_device->Present(0, 0, 0, 0);
}

void DxApp::clearBuffer(){
	_device->ColorFill(_surface, NULL, 0);
	_surface->LockRect(&lr,NULL,D3DLOCK_DISCARD);
	if (_zBuffer)
	{
		_zBuffer->clearBuffer();
	}
}

bool DxApp::testZBuffer( int x,int y,float z )
{
	if (_zBuffer)
	{
		return _zBuffer->test(x,y,z);
	}else{
		return true;
	}
}

void DxApp::initZBuffer()
{
	if (!_zBuffer)
	{
		_zBuffer = new ZBuffer(_w,_h);
	}
}

